//
//  Level.cpp
//  NinjaTurtle
//
//  Created by Nguyen Tri Chung on 1/27/15.
//
//

#include "Level.h"

Level::Level(int level) {
    this->autorelease();
    this->level = level;
    this->mapInfo = NULL;
    this->turtle = NULL;
    
    this->lands = new Vector<Obj*>();
    this->pushObjects = new Vector<StaticObj*>();
    this->treeObjects = new Vector<StaticObj*>();
    this->teleportObjects = new Vector<StaticObj*>();
    this->finishObjects = new Vector<Obj*>();
    this->hintObjects = new Vector<CommonObj*>();
    this->trapObjects = new Vector<CommonObj*>();
    this->enemyObjects = new Vector<EnemyObj*>();
    this->bulletObjects = new Vector<CommonObj*>();
    
    this->interactedHint = NULL;
}

Level::~Level() {
    CC_SAFE_RELEASE(this->turtle);
    CC_SAFE_RELEASE(this->mapInfo);
    CC_SAFE_DELETE(this->lands);
    CC_SAFE_DELETE(this->pushObjects);
    CC_SAFE_DELETE(this->treeObjects);
    CC_SAFE_DELETE(this->teleportObjects);
    CC_SAFE_DELETE(this->finishObjects);
    CC_SAFE_DELETE(this->hintObjects);
    CC_SAFE_DELETE(this->trapObjects);
    CC_SAFE_DELETE(this->enemyObjects);
    CC_SAFE_DELETE(this->bulletObjects);
}

void Level::loadMap() {
    char tmxFile[200];
    sprintf(tmxFile, DOC_MAP_TMX, this->level);
    this->mapInfo = TMXTiledMap::create(tmxFile);
    CC_SAFE_RETAIN(this->mapInfo);
    
    // set accel world
    this->accelWorld = CONFIG_ACCEL_WORLD;
    
    // set map size
    Size mapTileCount = this->mapInfo->getMapSize();
    Size mapTileSize = this->mapInfo->getTileSize();
    this->tileSize = mapTileSize;
    this->mapSize = Size(mapTileCount.width * mapTileSize.width, mapTileCount.height * mapTileSize.height);
    
    // create land objects
    TMXObjectGroup* landGroup = this->mapInfo->getObjectGroup("land");
    ValueVector lands = landGroup->getObjects();
    for (int i = 0; i < lands.size(); i++) {
        ValueMap land = lands.at(i).asValueMap();
        float width = land.at("width").asFloat();
        float height = land.at("height").asFloat();
        float x = land.at("x").asFloat();
        float y = land.at("y").asFloat();
        
        string name = land.at("name").asString();
        string properties = land.at("type").asString();
        if (name == OBJECT_PLATFORM) {
            RectBody* rectBody = new RectBody(width, height);
            rectBody->setPosition(Point(x + width / 2, y + height / 2));
            rectBody->setPlatform(true);
            CC_SAFE_RETAIN(rectBody);
            
            Obj* platformObj = new Obj();
            platformObj->setBody(rectBody);
            this->lands->pushBack(platformObj);
        } else if (name == OBJECT_TREE) {
            vector<string> animations;
            animations.push_back(FRAME_TREE_IDLE);
            animations.push_back(FRAME_TREE_CUT_OFF);
            StaticObj* treeObj = new StaticObj(FRAME_TREE_IDLE, animations);
            treeObj->setObjectName(name);
            treeObj->setPosition(Point(x + width / 2, y + height / 2));
            this->lands->pushBack(treeObj);
            this->treeObjects->pushBack(treeObj);
        } else if (name == OBJECT_ROCK) {
            StaticObj* rockObj = new StaticObj(FRAME_ROCK);
            rockObj->setObjectName(name);
            rockObj->setPosition(Point(x + width / 2, y + height / 2));
            
            this->lands->pushBack(rockObj);
            this->pushObjects->pushBack(rockObj);
        } else if (name == OBJECT_LEFT_BELT) {
            CommonObj* belt = new CommonObj(FRAME_LEFT_BELT, FRAME_LEFT_BELT);
            belt->setPosition(Point(x + width / 2, y + height / 2));
            this->updateProperties(properties, belt);
            this->lands->pushBack(belt);
        } else if (name == OBJECT_MID_BELT) {
            CommonObj* belt = new CommonObj(FRAME_MID_BELT, FRAME_MID_BELT);
            belt->setPosition(Point(x + width / 2, y + height / 2));
            this->updateProperties(properties, belt);
            this->lands->pushBack(belt);
        } else if (name == OBJECT_RIGHT_BELT) {
            CommonObj* belt = new CommonObj(FRAME_RIGHT_BELT, FRAME_RIGHT_BELT);
            belt->setPosition(Point(x + width / 2, y + height / 2));
            this->updateProperties(properties, belt);
            this->lands->pushBack(belt);
        } else {
            RectBody* rectBody = new RectBody(width, height);
            rectBody->setPosition(Point(x + width / 2, y + height / 2));
            CC_SAFE_RETAIN(rectBody);
            
            Obj* landObj = new Obj();
            landObj->setBody(rectBody);
            this->lands->pushBack(landObj);
        }
        
        if (name != OBJECT_PLATFORM &&
            name != OBJECT_ROCK &&
            name != OBJECT_TREE &&
            name != OBJECT_LEFT_BELT &&
            name != OBJECT_MID_BELT &&
            name != OBJECT_RIGHT_BELT &&
            name != "") {
            
            assert(NULL);
        }
    }
    
    TMXObjectGroup* objectGroup = this->mapInfo->getObjectGroup("object");
    ValueVector objects = objectGroup->getObjects();
    for (int i = 0; i < objects.size(); i++) {
        ValueMap object = objects.at(i).asValueMap();
        float width = object.at("width").asFloat();
        float height = object.at("height").asFloat();
        float x = object.at("x").asFloat();
        float y = object.at("y").asFloat();
        string type = object.at("name").asString();
        string properties = object.at("type").asString();
        
        if (type == OBJECT_FINISH) {
            RectBody* rectBody = new RectBody(width, height);
            rectBody->setPosition(Point(x + width / 2, y + height / 2));
            CC_SAFE_RETAIN(rectBody);
            
            Obj* obj = new Obj();
            obj->setBody(rectBody);
            this->finishObjects->pushBack(obj);
        } else if (type == OBJECT_PLAYER) {
            this->turtle = new Turtle();
            CC_SAFE_RETAIN(this->turtle);
            this->turtle->setPosition(Point(x + width / 2, y + height / 2));
            this->updateProperties(properties, this->turtle);
        } else if (type == OBJECT_HINT) {
            CommonObj* commonObj = new CommonObj(FRAME_HINT, FRAME_HINT);
            commonObj->setPosition(Point(x + width / 2, y + height / 2));
            this->updateProperties(properties, commonObj);
            this->hintObjects->pushBack(commonObj);
        } else if (type == OBJECT_SPIKE) {
            CommonObj* commonObj = new CommonObj(FRAME_SPIKE, FRAME_SPIKE);
            commonObj->setPosition(Point(x + width / 2, y + height / 2));
            this->updateProperties(properties, commonObj);
            this->trapObjects->pushBack(commonObj);
        } else if (type == OBJECT_AIR) {
            EnemyObj* enemyObj = new EnemyObj(FRAME_AIR, FRAME_AIR);
            enemyObj->setPosition(Point(x + width / 2, y + height / 2));
            enemyObj->setEnemyName(type);
            this->updateProperties(properties, enemyObj);
            this->enemyObjects->pushBack(enemyObj);
        } else if (type == OBJECT_NAIR) {
            EnemyObj* enemyObj = new EnemyObj(FRAME_NAIR, FRAME_NAIR);
            enemyObj->setPosition(Point(x + width / 2, y + height / 2));
            enemyObj->setEnemyName(type);
            this->updateProperties(properties, enemyObj);
            this->enemyObjects->pushBack(enemyObj);
        } else if (type == OBJECT_AIM) {
            EnemyObj* enemyObj = new EnemyObj(FRAME_AIM, FRAME_AIM);
            enemyObj->setPosition(Point(x + width / 2, y + height / 2));
            enemyObj->setEnemyName(type);
            this->updateProperties(properties, enemyObj);
            this->enemyObjects->pushBack(enemyObj);
        } else if (type == OBJECT_CRUSHER) {
            EnemyObj* enemyObj = new EnemyObj(FRAME_CRUSHER_IDLE, FRAME_CRUSHER_CRUSH);
            enemyObj->setPosition(Point(x + width / 2, y + height / 2));
            enemyObj->setEnemyName(type);
            this->updateProperties(properties, enemyObj);
            this->enemyObjects->pushBack(enemyObj);
        } else if (type == EQUIPMENT_FAST) {
            vector<string> animations;
            animations.push_back(FRAME_FAST_IDLE);
            animations.push_back(FRAME_FAST_MOVE);
            EnemyObj* enemyObj = new EnemyObj(FRAME_FAST_IDLE, animations);
            enemyObj->setPosition(Point(x + width / 2, y + height / 2));
            enemyObj->setEnemyName(type);
            this->updateProperties(properties, enemyObj);
            this->enemyObjects->pushBack(enemyObj);
        } else if (type == EQUIPMENT_STRENGTH) {
            vector<string> animations;
            animations.push_back(FRAME_STRENGTH_IDLE);
            animations.push_back(FRAME_STRENGTH_MOVE);
            EnemyObj* enemyObj = new EnemyObj(FRAME_STRENGTH_IDLE, animations);
            enemyObj->setPosition(Point(x + width / 2, y + height / 2));
            enemyObj->setEnemyName(type);
            this->updateProperties(properties, enemyObj);
            this->enemyObjects->pushBack(enemyObj);
        } else if (type == EQUIPMENT_WARRIOR) {
            vector<string> animations;
            animations.push_back(FRAME_WARRIOR_IDLE);
            animations.push_back(FRAME_WARRIOR_MOVE);
            animations.push_back(FRAME_WARRIOR_THROW);
            EnemyObj* enemyObj = new EnemyObj(FRAME_WARRIOR_IDLE, animations);
            enemyObj->setPosition(Point(x + width / 2, y + height / 2));
            enemyObj->setEnemyName(type);
            this->updateProperties(properties, enemyObj);
            this->enemyObjects->pushBack(enemyObj);
        } else if (type == EQUIPMENT_DEATH) {
            vector<string> animations;
            animations.push_back(FRAME_DEATH_IDLE);
            animations.push_back(FRAME_DEATH_MOVE);
            animations.push_back(FRAME_DEATH_THROW);
            EnemyObj* enemyObj = new EnemyObj(FRAME_DEATH_IDLE, animations);
            enemyObj->setPosition(Point(x + width / 2, y + height / 2));
            enemyObj->setEnemyName(type);
            this->updateProperties(properties, enemyObj);
            this->enemyObjects->pushBack(enemyObj);
        } else if (type == EQUIPMENT_NINJA) {
            vector<string> animations;
            animations.push_back(FRAME_NINJA_IDLE);
            animations.push_back(FRAME_NINJA_MOVE);
            EnemyObj* enemyObj = new EnemyObj(FRAME_NINJA_IDLE, animations);
            enemyObj->setPosition(Point(x + width / 2, y + height / 2));
            enemyObj->setEnemyName(type);
            this->updateProperties(properties, enemyObj);
            this->enemyObjects->pushBack(enemyObj);
        } else if (type == EQUIPMENT_ANGEL) {
            vector<string> animations;
            animations.push_back(FRAME_ANGEL_IDLE);
            animations.push_back(FRAME_ANGEL_MOVE);
            EnemyObj* enemyObj = new EnemyObj(FRAME_ANGEL_IDLE, animations);
            enemyObj->setPosition(Point(x + width / 2, y + height / 2));
            enemyObj->setEnemyName(type);
            this->updateProperties(properties, enemyObj);
            this->enemyObjects->pushBack(enemyObj);
        } else if (type == OBJECT_TELEPORT) {
            StaticObj* staticObj = new StaticObj(FRAME_TELEPORT, FRAME_TELEPORT);
            staticObj->setObjectName(type);
            staticObj->setPosition(Point(x + width / 2, y + height / 2));
            this->updateProperties(properties, staticObj);
            this->teleportObjects->pushBack(staticObj);
        }
        
        if (type != OBJECT_FINISH &&
            type != OBJECT_PLAYER &&
            type != OBJECT_HINT &&
            type != OBJECT_SPIKE &&
            type != OBJECT_AIR &&
            type != OBJECT_NAIR &&
            type != OBJECT_AIM &&
            type != OBJECT_CRUSHER &&
            type != OBJECT_TELEPORT &&
            type != EQUIPMENT_WARRIOR &&
            type != EQUIPMENT_DEATH &&
            type != EQUIPMENT_NINJA &&
            type != EQUIPMENT_ANGEL &&
            type != EQUIPMENT_FAST &&
            type != EQUIPMENT_STRENGTH &&
            type != "") {
            
            assert(NULL);
        }
    }
}

void Level::updateObjectTeleport(float dt) {
    GameModel* gameModel = GameModel::getInstance();
    RectBody* turtleBody = this->turtle->getRectBody();
    RectBody* smallerTurtleBody = new RectBody(1, 1);
    smallerTurtleBody->setPosition(turtleBody->getPosition());
    smallerTurtleBody->setRotation(turtleBody->getRotation());
    
    int numberInteractWithTeleport = 0;
    for (auto iter = this->teleportObjects->begin(); iter != this->teleportObjects->end(); iter++) {
        StaticObj* teleportObj = *iter;
        RectBody* teleportBody = teleportObj->getRectBody();
        RectBody* smallerTeleportBody = new RectBody(teleportBody->getWidth(), teleportBody->getHeight());
        smallerTeleportBody->setPosition(teleportBody->getPosition());
        smallerTeleportBody->setRotation(teleportBody->getRotation());
        
        if (CPointUtil::b2Interact(smallerTeleportBody, smallerTurtleBody)) {
            numberInteractWithTeleport++;
            if (!this->turtle->getInsideTeleport()) {
                for (auto iter2 = this->teleportObjects->begin(); iter2 != this->teleportObjects->end(); iter2++) {
                    StaticObj* otherTeleportObj = *iter2;
                    if (otherTeleportObj->getTeleportAddress() == teleportObj->getTeleportDestination()) {
                        this->turtle->setPosition(otherTeleportObj->getPosition());
                        this->turtle->setInsideTeleport(true);
                        this->turtle->setMoveState(MOVE_NONE);
                        this->turtle->setLastStepY(this->turtle->getPosition().y);
                        
                        GameEvent* gameEvent = new GameEvent();
                        gameEvent->setEventCode(EVT_TURTLE_TELEPORT);
                        gameModel->fireEvent(gameEvent);
                        
                        break;
                    }
                }
            }
        }
    }
    
    if (numberInteractWithTeleport == 0) {
        this->turtle->setInsideTeleport(false);
    }
}

void Level::updateObjectTree(float dt) {
    GameModel* gameModel = GameModel::getInstance();
    __Array* treeToDelete = __Array::create();
    
    for (auto iter = this->treeObjects->begin(); iter != this->treeObjects->end(); iter++) {
        StaticObj* treeObject = *iter;
        
        string frameState = FRAME_TREE_IDLE;
        if (treeObject->getCurrentDelayToBreak() == 0) {
            // check interact with turtle
            if (this->turtle->getEquipment() == EQUIPMENT_NINJA &&
                CPointUtil::b2Interact(treeObject->getRectBody(), this->turtle->getRectBody())) {
                
                frameState = FRAME_TREE_CUT_OFF;
                CSprite* spriteMain = treeObject->getSprite(SPRITE_TYPE_MAIN);
                ActionInterval* actionCutOff = (ActionInterval*) spriteMain->getAction(frameState, false);
                float delayToBreak = actionCutOff->getDuration();
                treeObject->setCurrentDelayToBreak(delayToBreak);
                
                GameEvent* gameEvent = new GameEvent();
                gameEvent->setEventCode(EVT_TURTLE_SLASH);
                gameModel->fireEvent(gameEvent);
                
                this->turtle->setSlashing(true);
            }
        } else {
            frameState = FRAME_TREE_CUT_OFF;
            float currentDelayToBreak = treeObject->getCurrentDelayToBreak();
            float newCurrentDelayToBreak = currentDelayToBreak - dt;
            if (newCurrentDelayToBreak < 0) {
                treeToDelete->addObject(treeObject);
                this->turtle->setSlashing(false);
            }
            treeObject->setCurrentDelayToBreak(newCurrentDelayToBreak);
        }
        treeObject->play(SPRITE_TYPE_MAIN, frameState);
    }
    
    for (auto iter = treeToDelete->begin(); iter != treeToDelete->end(); iter++) {
        StaticObj* treeObject = (StaticObj*) *iter;
        DataEvent* dataEvent = new DataEvent();
        dataEvent->setEventCode(EVT_TREE_BEING_CUT);
        dataEvent->setArgumentReference(treeObject);
        gameModel->fireEvent(dataEvent);
        this->lands->eraseObject(treeObject);
        this->treeObjects->eraseObject(treeObject);
    }
}

void Level::updateObjectPushing(float dt) {
    for (auto iter = this->pushObjects->begin(); iter != this->pushObjects->end(); iter++) {
        StaticObj* pushObj = *iter;
        RectBody* pushObjBody = pushObj->getRectBody();
        pushObj->update(dt);
        pushObj->setJumping(true);
        pushObj->setBlocking(false);
        
        // falling
        pushObj->setCurrentElapsedJumpTime(pushObj->getCurrentElapsedJumpTime() + dt);
        float elapsedTime = pushObj->getCurrentElapsedJumpTime();
        float additionalHeight = ((-this->accelWorld * elapsedTime * elapsedTime) / 2);
        
        if (pushObj->getLastStepY() == -9999) {
            pushObj->setLastStepY(pushObj->getPosition().y);
        }
        float newPositionY = pushObj->getLastStepY() + additionalHeight;
        bool isFallDown = newPositionY < this->turtle->getPosition().y;
        pushObj->setPosition(Point(pushObj->getPosition().x, newPositionY));
        
        // update push obj limit
        Point pushObjectVelocity = Point::ZERO;
        Point objectViewPoint = this->getViewPoint(pushObj->getPosition());
        for (auto iter = this->lands->begin(); iter != this->lands->end(); iter++) {
            Obj* commonObj = *iter;
            if (!this->canSee2(commonObj->getRectBody(), objectViewPoint)) {
                continue;
            }
            if (pushObj == commonObj) {
                continue;
            }
            RectBody* land = commonObj->getRectBody();
            TouchDirection touchDirection;
            float limitValue;
            CPointUtil::checkLimit(pushObjBody, land, touchDirection, limitValue);
            if (touchDirection == TOUCH_LEFT) {
                pushObjBody->setPosition(Point(limitValue, pushObjBody->getPosition().y));
                pushObj->setBlocking(true);
            } else if (touchDirection == TOUCH_RIGHT) {
                pushObjBody->setPosition(Point(limitValue, pushObjBody->getPosition().y));
                pushObj->setBlocking(true);
            } else if (touchDirection == TOUCH_UP) {
                pushObjBody->setPosition(Point(pushObjBody->getPosition().x, limitValue));
                pushObj->setJumping(false);
                pushObj->setCurrentElapsedJumpTime(0);
                pushObj->setLastStepY(limitValue);
                if (pushObjectVelocity == Point::ZERO) {
                    pushObjectVelocity = Point(land->getBeltSpeed(), 0);
                }
            } else if (touchDirection == TOUCH_DOWN) {
                pushObjBody->setPosition(Point(pushObjBody->getPosition().x, limitValue));
            }
        }
        pushObjBody->setVelocity(pushObjectVelocity);
        
        // update postion of push object arcording to turtle position
        if (!pushObj->getJumping() &&
            this->turtle->getEquipment() == EQUIPMENT_STRENGTH &&
            !this->turtle->getJumping() &&
            this->turtle->getTargetJumpHeight() == -1) {
            
            RectBody* turtleBody = this->turtle->getRectBody();
            TouchDirection touchDirection;
            float limitValue;
            CPointUtil::checkLimit(pushObjBody, turtleBody, touchDirection, limitValue);
            if (touchDirection == TOUCH_LEFT) {
                pushObjBody->setPosition(Point(limitValue, pushObjBody->getPosition().y));
            } else if (touchDirection == TOUCH_RIGHT) {
                pushObjBody->setPosition(Point(limitValue, pushObjBody->getPosition().y));
            }
        }
        
        // set push object falling
        if (isFallDown && pushObj->getJumping()) {
            pushObj->setFalling(true);
        } else {
            pushObj->setFalling(false);
        }
    }
}

void Level::updateTurtle(float dt) {
    GameModel* gameModel = GameModel::getInstance();
    this->turtle->update(dt);
    this->turtle->setJumping(true);
    bool frameStateLoop = false;
    string frameState = this->turtle->getStateIdle();
    
    // handle tutorial
    if (this->interactedHint != NULL) {
        if (!CPointUtil::b2Interact(this->interactedHint->getRectBody(), this->turtle->getRectBody())) {
            this->interactedHint = NULL;
            DataEvent* dataEvent = new DataEvent();
            dataEvent->setEventCode(EVT_LOSE_HINT);
            gameModel->fireEvent(dataEvent);
        }
    } else {
        for (auto iter = this->hintObjects->begin(); iter != this->hintObjects->end(); iter++) {
            CommonObj* commonObj = *iter;
            if (!this->canSee(commonObj->getRectBody())) {
                continue;
            }
            if (CPointUtil::b2Interact(commonObj->getRectBody(), this->turtle->getRectBody())) {
                this->interactedHint = commonObj;
                DataEvent* dataEvent = new DataEvent();
                dataEvent->setEventCode(EVT_RECEIVE_HINT);
                dataEvent->setArgumentString(commonObj->getValueString());
                gameModel->fireEvent(dataEvent);
                break;
            }
        }
    }
    
    // handle trap
    for (auto iter = this->trapObjects->begin(); iter != this->trapObjects->end(); iter++) {
        CommonObj* commonObj = *iter;
        if (!this->canSee(commonObj->getRectBody())) {
            continue;
        }
        RectBody* smallerBody = new RectBody(this->turtle->getRectBody()->getWidth() / 2, this->turtle->getRectBody()->getHeight() / 2);
        smallerBody->setPosition(this->turtle->getRectBody()->getPosition());
        smallerBody->setRotation(this->turtle->getRectBody()->getRotation());
        
        if (CPointUtil::b2Interact(commonObj->getRectBody(), smallerBody)) {
            this->turtle->setDead(true);
            break;
        }
    }
    
    // handle finish
    for (auto iter = this->finishObjects->begin(); iter != this->finishObjects->end(); iter++) {
        Obj* obj = *iter;
        if (!this->canSee(obj->getRectBody())) {
            continue;
        }
        
        RectBody* smallerBody = new RectBody(1, 1);
        smallerBody->setPosition(this->turtle->getRectBody()->getPosition());
        smallerBody->setRotation(this->turtle->getRectBody()->getRotation());
        
        if (CPointUtil::b2Interact(obj->getRectBody(), smallerBody)) {
            this->turtle->setDisappear(true);
            break;
        }
    }
    
    // update turtle delay throw
    if (this->turtle->getCurrentThrowDelay() > 0) {
        this->turtle->setCurrentThrowDelay(this->turtle->getCurrentThrowDelay() - dt);
        if (this->turtle->getCurrentThrowDelay() <= 0) {
            this->turtle->setThrowing(false);
        }
    }
    
    // handle jump
    float lengthJump = 0;
    if (this->turtle->getTargetJumpHeight() != -1) {
        float minY = 0;
        float maxY = this->turtle->getTargetJumpHeight();
        lengthJump = minY - maxY;
    }
    
    this->turtle->setCurrentElapsedJumpTime(this->turtle->getCurrentElapsedJumpTime() + dt);
    float velocityY = sqrt(2 *  lengthJump * -this->accelWorld);
    float elapsedTime = this->turtle->getCurrentElapsedJumpTime();
    float additionalHeight = velocityY * elapsedTime + ((-this->accelWorld * elapsedTime * elapsedTime) / 2);
    
    if (this->turtle->getLastStepY() == -9999) {
        this->turtle->setLastStepY(this->turtle->getPosition().y);
    }
    float newPositionY = this->turtle->getLastStepY() + additionalHeight;
    bool isFallDown = newPositionY < this->turtle->getPosition().y;
    this->turtle->setPosition(Point(this->turtle->getPosition().x, newPositionY));
    
    // pre check collision to identify turtle just collide with only one land
//    int numberInteract = 0;
//    RectBody* turtleBody = (RectBody*) this->turtle->getBody();
//    for (auto iter = this->lands->begin(); iter != this->lands->end(); iter++) {
//        Obj* land = *iter;
//        if (CPointUtil::b2Interact(this->turtle->getRectBody(), land->getRectBody())) {
//            numberInteract++;
//            if (numberInteract > 1) {
//                break;
//            }
//        }
//    }
    
    // start check collision with lands
    RectBody* turtleBody = (RectBody*) this->turtle->getBody();
    bool hasInteractWithStaticObj = false;
    int numberInteract = 0;
    for (auto iter = this->lands->begin(); iter != this->lands->end(); iter++) {
        Obj* landObj = *iter;
        if (!this->canSee(landObj->getRectBody())) {
            continue;
        }
        
        if (CPointUtil::b2Interact(landObj->getRectBody(), this->turtle->getRectBody())) {
            numberInteract++;
            if (numberInteract > 1) {
                break;
            }
        }
    }
    
    for (auto iter = this->lands->begin(); iter != this->lands->end(); iter++) {
        Obj* landObj = *iter;
        if (!this->canSee(landObj->getRectBody())) {
            continue;
        }
        
        bool isPushable = false;
        StaticObj* staticObj = dynamic_cast<StaticObj*>(landObj);
        if (staticObj != NULL) {
            if (staticObj->getObjectName() == OBJECT_ROCK &&
                this->turtle->getEquipment() == EQUIPMENT_STRENGTH) {
                isPushable = true;
                if (staticObj->getFalling()) {
                    isPushable = false;
                }
                if (staticObj->getBlocking()) {
                    isPushable = false;
                }
            }
        }
        RectBody* land = landObj->getRectBody();
        TouchDirection touchDirection;
        float limitValue;
        CPointUtil::checkLimit(this->turtle->getRectBody(), land, touchDirection, limitValue);
        if (numberInteract > 1) {
            float minXLand = land->getPosition().x - land->getWidth() / 2;
            float maxXLand = land->getPosition().x + land->getWidth() / 2;
            float minYLand = land->getPosition().y - land->getHeight() / 2;
            float maxYLand = land->getPosition().y + land->getHeight() / 2;
            
            if (touchDirection == TOUCH_LEFT) {
                // check if land not platform and will set limit when can't push it
                if (turtleBody->getPosition().y > minYLand &&
                    turtleBody->getPosition().y < maxYLand &&
                    !land->getPlatform()) {
                    
                    if (!isPushable) {
                        turtleBody->setPosition(Point(limitValue, this->turtle->getPosition().y));
                    } else {
                        hasInteractWithStaticObj = true;
                    }
                }
            } else if (touchDirection == TOUCH_RIGHT) {
                // check if land not platform and will set limit when can't push it
                if (turtleBody->getPosition().y > minYLand &&
                    turtleBody->getPosition().y < maxYLand &&
                    !land->getPlatform()) {
                    
                    if (!isPushable) {
                        turtleBody->setPosition(Point(limitValue, this->turtle->getPosition().y));
                    } else {
                        hasInteractWithStaticObj = true;
                    }
                }
            } else if (touchDirection == TOUCH_UP) {
                // check turtle falling and interact with land
                if (turtleBody->getPosition().x > minXLand &&
                    turtleBody->getPosition().x < maxXLand &&
                    isFallDown) {
                    
                    turtleBody->setPosition(Point(this->turtle->getPosition().x, limitValue));
                    this->turtle->setJumping(false);
                    this->turtle->setTargetJumpHeight(-1);
                    this->turtle->setCurrentElapsedJumpTime(0);
                    this->turtle->setLastStepY(limitValue);
                    this->turtle->setPerformingSecondJump(false);
                    turtleBody->setVelocity(turtleBody->getVelocity() + Point(land->getBeltSpeed(), 0));
                }
            } else if (touchDirection == TOUCH_DOWN) {
                // check if land not platform, and check if turtle jump will hit ceiling
                if (turtleBody->getPosition().x > minXLand &&
                    turtleBody->getPosition().x < maxXLand &&
                    !land->getPlatform()) {
                    
                    turtleBody->setPosition(Point(this->turtle->getPosition().x, limitValue));
                    this->turtle->setTargetJumpHeight(-1);
                    this->turtle->setCurrentElapsedJumpTime(0);
                    this->turtle->setLastStepY(limitValue);
                }
            }
        } else {
            if (touchDirection == TOUCH_LEFT) {
                // check if not platform
                if (!land->getPlatform()) {
                    if (!isPushable) {
                        turtleBody->setPosition(Point(limitValue, this->turtle->getPosition().y));
                    } else {
                        hasInteractWithStaticObj = true;
                    }
                }
            } else if (touchDirection == TOUCH_RIGHT) {
                // check if not platform
                if (!land->getPlatform()) {
                    if (!isPushable) {
                        turtleBody->setPosition(Point(limitValue, this->turtle->getPosition().y));
                    } else {
                        hasInteractWithStaticObj = true;
                    }
                }
            } else if (touchDirection == TOUCH_UP) {
                // check turtle falling and interact with land
                if (isFallDown) {
                    turtleBody->setPosition(Point(this->turtle->getPosition().x, limitValue));
                    this->turtle->setJumping(false);
                    this->turtle->setTargetJumpHeight(-1);
                    this->turtle->setCurrentElapsedJumpTime(0);
                    this->turtle->setLastStepY(limitValue);
                    this->turtle->setPerformingSecondJump(false);
                    turtleBody->setVelocity(turtleBody->getVelocity() + Point(land->getBeltSpeed(), 0));
                }
            } else if (touchDirection == TOUCH_DOWN) {
                // check if land not platform, and check if turtle jump will hit ceiling
                if (!land->getPlatform()) {
                    turtleBody->setPosition(Point(this->turtle->getPosition().x, limitValue));
                    this->turtle->setTargetJumpHeight(-1);
                    this->turtle->setCurrentElapsedJumpTime(0);
                    this->turtle->setLastStepY(limitValue);
                }
            }
        }
    }
    
    if (hasInteractWithStaticObj) {
        Point turtleVelocity = turtleBody->getVelocity();
        if (turtleVelocity != Point::ZERO) {
            turtleVelocity.x = turtleVelocity.x / 10;
            turtleBody->setVelocity(turtleVelocity);
        }
    }
    
    // check animation
    if (this->turtle->getDisappear()) {
        frameState = this->turtle->getStateDie();
        frameStateLoop = false;
    } else if (this->turtle->getDead()) {
        frameState = this->turtle->getStateDie();
        frameStateLoop = false;
    } else if (this->turtle->getThrowing()) {
        frameState = this->turtle->getStateThrow();
        frameStateLoop = false;
    } else if (this->turtle->getSlashing()) {
        frameState = this->turtle->getStateSlash();
        frameStateLoop = false;
    } else if (isFallDown && this->turtle->getJumping()) {
        frameState = this->turtle->getStateFall();
        frameStateLoop = false;
    } else if (this->turtle->getTargetJumpHeight() != -1 && this->turtle->getJumping()) {
        frameState = this->turtle->getStateJump();
        frameStateLoop = false;
    } else if (this->turtle->getMoveState() != MOVE_NONE) {
        frameState = this->turtle->getStateMove();
        if (hasInteractWithStaticObj && this->turtle->getEquipment() == EQUIPMENT_STRENGTH) {
            frameState = this->turtle->getStatePush();
        }
        frameStateLoop = true;
    }
    
    if (frameStateLoop) {
        this->turtle->playLoop(SPRITE_TYPE_MAIN, frameState);
    } else {
        this->turtle->play(SPRITE_TYPE_MAIN, frameState);
    }
}

void Level::updateProperties(string propertyText, Obj* obj) {
    if (propertyText == "") {
        return;
    }
    vector<string> properties = CStringUtil::split(propertyText, ';');
    for (auto iter = properties.begin(); iter != properties.end(); iter++) {
        string property = *iter;
        vector<string> keyValue = CStringUtil::split(property, ':');
        string key = keyValue.at(0);
        string valueString = keyValue.at(1);
        if (key == "sdf") { // size default
            obj->getRectBody()->setContentSize(this->tileSize.width, this->tileSize.height);
        } else if (key == "ac") { // anchor
            if (valueString == "050") { // (0.5, 0)
                obj->getRectBody()->setAnchorPoint(Point(0.5f, 0));
            } else if (valueString == "051") { // 0.5, 1)
                obj->getRectBody()->setAnchorPoint(Point(0.5f, 1));
            }
        } else if (key == "fy") { // flip y
            obj->getRectBody()->setFlipY(true);
        } else if (key == "fx") { // flip x
            obj->getRectBody()->setFlipX(true);
        } else if (key == "ap") { // auto play
            if (valueString == "1") { // true
                CSprite* spriteMain = obj->getSprite(SPRITE_TYPE_MAIN);
                spriteMain->playLastAction(true);
            } else {
                CSprite* spriteMain = obj->getSprite(SPRITE_TYPE_MAIN);
                spriteMain->playLastAction(false);
            }
        } else if (key == "hint") { // hint description
            ((CommonObj*) obj)->setValueString(valueString);
        } else if (key == "eq") { // equipable
            ((EnemyObj*) obj)->setEquipable(true);
        } else if (key == "trp") { // trappable
            ((EnemyObj*) obj)->setTrappable(true);
        } else if (key == "wlk") { // walkable
            ((EnemyObj*) obj)->setWalkable(true);
            ((EnemyObj*) obj)->setMoveState(MOVE_LEFT);
        } else if (key == "mc") { //machiable
            ((EnemyObj*) obj)->setMachinable(true);
        } else if (key == "dlf") { // delay to fire
            ((EnemyObj*) obj)->setCurrentDelayToFire(atof(valueString.data()));
        } else if (key == "dfx") { // direction fire x
            ((EnemyObj*) obj)->setFireX(atof(valueString.data()));
        } else if (key == "dfy") { // direction fire y
            ((EnemyObj*) obj)->setFireY(atof(valueString.data()));
        } else if (key == "fr") { // fireable
            ((EnemyObj*) obj)->setFireable(true);
        } else if (key == "rot") { // rotate
            CommonObj* commonObj = (CommonObj*) obj;
            CSprite* spriteMain = commonObj->getSprite(SPRITE_TYPE_MAIN);
            float beltSpeed = CONFIG_BELT_SPEED;
            if (valueString == "right") { // right
                Action* action = spriteMain->getAction(spriteMain->getLastActionName(), true);
                Action* revertAction = action->reverse();
                spriteMain->resetAction(spriteMain->getLastActionName(), true, revertAction);
            } else {
                beltSpeed = -beltSpeed;
            }
            RectBody* rectBody = commonObj->getRectBody();
            rectBody->setBeltSpeed(beltSpeed);
            rectBody->setPosition(rectBody->getPosition() + Point(0, rectBody->getHeight() / 2));
            commonObj->playLoop(SPRITE_TYPE_MAIN, spriteMain->getLastActionName());
        } else if (key == "cr") { // crusherable
            ((EnemyObj*) obj)->setCrusherable(true);
        } else if (key == "aim") { // aimable
            ((EnemyObj*) obj)->setAimable(true);
        } else if (key == "n") { // name/address
            ((StaticObj*) obj)->setTeleportAddress(valueString);
        } else if (key == "g") { // go to
            ((StaticObj*) obj)->setTeleportDestination(valueString);
        }
    }
}

void Level::updateBullet(float dt) {
    GameModel* gameModel = GameModel::getInstance();
    __Array* bulletToDelete = __Array::create();
    for (auto iter = this->bulletObjects->begin(); iter != this->bulletObjects->end(); iter++) {
        bool interact = false;
        CommonObj* bullet = *iter;
        bullet->update(dt);
        
        if (bullet->getValueString() == BULLET_TYPE_ENEMY) {
            // check hit enemy
            for (auto iter2 = this->enemyObjects->begin(); iter2 != this->enemyObjects->end(); iter2++) {
                EnemyObj* enemyObj = *iter2;
                if (CPointUtil::b2Interact(bullet->getRectBody(), enemyObj->getRectBody())) {
                    if (this->canSee(enemyObj->getRectBody())) {
                        if (enemyObj->getEnemyName() == EQUIPMENT_DEATH ||
                            enemyObj->getEnemyName() == EQUIPMENT_FAST ||
                            enemyObj->getEnemyName() == EQUIPMENT_STRENGTH ||
                            enemyObj->getEnemyName() == EQUIPMENT_WARRIOR ||
                            enemyObj->getEnemyName() == EQUIPMENT_ANGEL ||
                            enemyObj->getEnemyName() == EQUIPMENT_NINJA) {
                            
                            GameEvent* gameEvent = new GameEvent();
                            gameEvent->setEventCode(EVT_BULLET_HIT_HUMAN);
                            gameModel->fireEvent(gameEvent);
                        } else {
                            GameEvent* gameEvent = new GameEvent();
                            gameEvent->setEventCode(EVT_BULLET_HIT_MACHINE);
                            gameModel->fireEvent(gameEvent);
                        }
                    }
                    
                    DataEvent* dataEvent = new DataEvent();
                    dataEvent->setEventCode(EVT_ENEMY_BEING_SHOOTED);
                    dataEvent->setArgumentReference(enemyObj);
                    gameModel->fireEvent(dataEvent);
                    this->enemyObjects->eraseObject(enemyObj);
                    interact = true;
                    break;
                }
            }
        } else {
            // check hit turtle
            if (!this->turtle->getDead() &&
                !this->turtle->getDisappear()) {
                
                RectBody* smallerBody = new RectBody(this->turtle->getRectBody()->getWidth() / 2, this->turtle->getRectBody()->getHeight() / 2);
                smallerBody->setPosition(this->turtle->getRectBody()->getPosition());
                smallerBody->setRotation(this->turtle->getRectBody()->getRotation());
                
                if (CPointUtil::b2Interact(bullet->getRectBody(), smallerBody)) {
                    this->turtle->setDead(true);
                    interact = true;;
                }
            }
        }
        
        // check hit land
        Point bulletViewPoint = this->getViewPoint(bullet->getPosition());
        if (!interact) {
            for (auto iter2 = this->lands->begin(); iter2 != this->lands->end(); iter2++) {
                Obj* land = *iter2;
                if (!this->canSee2(land->getRectBody(), bulletViewPoint)) {
                    continue;
                }
                
                if (land->getRectBody()->getPlatform()) {
                    continue;
                }
                
                if (CPointUtil::b2Interact(bullet->getRectBody(), land->getRectBody())) {
                    if (this->canSee(land->getRectBody())) {
                        GameEvent* gameEvent = new GameEvent();
                        gameEvent->setEventCode(EVT_BULLET_HIT_LAND);
                        gameModel->fireEvent(gameEvent);
                    }
                    interact = true;
                    break;
                }
            }
        }
        
        if (interact) {
            bulletToDelete->addObject(bullet);
        }
    }
    
    for (auto iter = bulletToDelete->begin(); iter != bulletToDelete->end(); iter++) {
        CommonObj* bullet = (CommonObj*) (*iter);
        
        DataEvent* dataEvent = new DataEvent();
        dataEvent->setEventCode(EVT_LOST_BULLET);
        dataEvent->setArgumentReference(bullet);
        gameModel->fireEvent(dataEvent);
        
        this->bulletObjects->eraseObject(bullet);
    }
}

void Level::update(float dt) {
    // update enemies
    for (auto iter = this->enemyObjects->begin(); iter != this->enemyObjects->end(); iter++) {
        EnemyObj* enemy = *iter;
        if (enemy->getWalkable()) {
            this->updateEnemyRunner(enemy, dt);
        } else if (enemy->getMachinable()) {
            this->updateEnemyMachine(enemy, dt);
        } else if (enemy->getCrusherable()) {
            this->updateEnemyCrusher(enemy, dt);
        }
    }
    
    // update bullets
    this->updateBullet(dt);
    
    // if die no velocity
    if (this->turtle->getDead() || this->turtle->getDisappear()) {
        this->turtle->getBody()->setVelocity(Point::ZERO);
    }
    
    // update turtle
    this->updateTurtle(dt);
    
    // update static objects
    this->updateObjectPushing(dt);
    
    // update tree objects
    this->updateObjectTree(dt);
    
    // update teleport objects
    this->updateObjectTeleport(dt);
    
    // update view point arcording to turtle
    this->updateViewPoint();
}

void Level::updateEnemyCrusher(EnemyObj* enemy, float dt) {
    GameModel* gameModel = GameModel::getInstance();
    enemy->update(dt);
    string frameState = FRAME_CRUSHER_IDLE;
    
    bool endAction = false;
    if (enemy->fire(dt)) {
        frameState = FRAME_CRUSHER_CRUSH;
        CSprite* spriteMain = enemy->getSprite(SPRITE_TYPE_MAIN);
        ActionInterval* actionCrush = (ActionInterval*) spriteMain->getAction(frameState, false);
        float delayToCrush = actionCrush->getDuration();
        enemy->setCurrentDelayToCrush(delayToCrush);
        enemy->setMaxDelayToCrush(delayToCrush);
        
        if (this->canSee(enemy->getRectBody())) {
            GameEvent* gameEvent = new GameEvent();
            gameEvent->setEventCode(EVT_CRUSH);
            gameModel->fireEvent(gameEvent);
        }
    } else {
        if (enemy->getCurrentDelayToCrush() > 0) {
            frameState = FRAME_CRUSHER_CRUSH;
            float currentDelayToCrush = enemy->getCurrentDelayToCrush();
            float newCurrentDelayToCrush = currentDelayToCrush - dt;
            if (newCurrentDelayToCrush < 0) {
                endAction = true;
            }
            if (newCurrentDelayToCrush > enemy->getMaxDelayToCrush() / 2) {
                RectBody* smallerBody = new RectBody(this->turtle->getRectBody()->getWidth() / 2, this->turtle->getRectBody()->getHeight() / 2);
                smallerBody->setPosition(this->turtle->getRectBody()->getPosition());
                smallerBody->setRotation(this->turtle->getRectBody()->getRotation());
                
                if (CPointUtil::b2Interact(enemy->getRectBody(), smallerBody)) {
                    this->turtle->setDead(true);
                }
            }
            enemy->setCurrentDelayToCrush(newCurrentDelayToCrush);
        }
    }
    enemy->play(SPRITE_TYPE_MAIN, frameState);
    if (endAction) {
        enemy->setCurrentDelayToFire(CONFIG_DELAY_PER_FIRE_CRUSHER);
    }
}

void Level::updateEnemyMachine(EnemyObj* enemy, float dt) {
    GameModel* gameModel = GameModel::getInstance();
    enemy->update(dt);
    
    if (enemy->fire(dt)) {
        CommonObj* bulletObject = NULL;
        if (enemy->getAimable()) {
            enemy->setCurrentDelayToFire(CONFIG_DELAY_PER_FIRE_AIMBOT);
            
            if (this->canSee(enemy->getRectBody())) {
                bulletObject = new CommonObj();
                bulletObject->initWithFrame(FRAME_BULLET_3);
                bulletObject->setPosition(enemy->getPosition());
                
                Point vectorToTurtle = Point(this->turtle->getPosition() - bulletObject->getPosition());
                float angle = atan2(vectorToTurtle.x, vectorToTurtle.y);
                float velocityX = CONFIG_FIRE_SPEED_AIMBOT * sin(angle);
                float velocityY = CONFIG_FIRE_SPEED_AIMBOT * cos(angle);
                bulletObject->getBody()->setVelocity(Point(velocityX, velocityY));
                this->bulletObjects->pushBack(bulletObject);
            }
        } else {
            enemy->setCurrentDelayToFire(CONFIG_DELAY_PER_FIRE_MACHINE);
            if (this->canSee(enemy->getRectBody())) {
                if (enemy->getFireY() < 0) {
                    bulletObject = new CommonObj();
                    bulletObject->initWithFrame(FRAME_BULLET_2);
                    bulletObject->getBody()->setVelocity(Point(enemy->getFireX(), enemy->getFireY()));
                    
                    bulletObject->setPosition(Point(enemy->getPosition().x, enemy->getPosition().y - enemy->getRectBody()->getHeight() / 2 - bulletObject->getRectBody()->getHeight() / 2));
                } else if (enemy->getFireX() > 0) {
                    bulletObject = new CommonObj();
                    bulletObject->initWithFrame(FRAME_BULLET_1);
                    bulletObject->getBody()->setVelocity(Point(enemy->getFireX(), enemy->getFireY()));
                    
                    bulletObject->setPosition(Point(enemy->getPosition().x + enemy->getRectBody()->getWidth() / 2 + bulletObject->getRectBody()->getWidth() / 2, enemy->getPosition().y));
                } else if (enemy->getFireX() < 0) {
                    bulletObject = new CommonObj();
                    bulletObject->initWithFrame(FRAME_BULLET_1);
                    bulletObject->getBody()->setVelocity(Point(enemy->getFireX(), enemy->getFireY()));
                    bulletObject->getBody()->setFlipX(true);
                    
                    bulletObject->setPosition(Point(enemy->getPosition().x - enemy->getRectBody()->getWidth() / 2 - bulletObject->getRectBody()->getWidth() / 2, enemy->getPosition().y));
                }
                this->bulletObjects->pushBack(bulletObject);
            }
        }
        
        if (bulletObject != NULL) {
            DataEvent* dataEvent = new DataEvent();
            dataEvent->setEventCode(EVT_NEW_BULLET);
            dataEvent->setArgumentReference(bulletObject);
            gameModel->fireEvent(dataEvent);
        }
    }
}

void Level::updateEnemyRunner(EnemyObj* enemy, float dt) {
    GameModel* gameModel = GameModel::getInstance();
    enemy->update(dt);
    bool frameStateLoop = true;
    string frameState = enemy->getStateMove();
    
    RectBody* enemyBody = (RectBody*) enemy->getBody();
    RectBody* futureBody = new RectBody(enemyBody->getWidth(), enemyBody->getHeight());
    
    // falling
    enemy->setCurrentElapsedJumpTime(enemy->getCurrentElapsedJumpTime() + dt);
    float elapsedTime = enemy->getCurrentElapsedJumpTime();
    float additionalHeight = ((-this->accelWorld * elapsedTime * elapsedTime) / 2);
    
    if (enemy->getLastStepY() == -9999) {
        enemy->setLastStepY(enemy->getPosition().y);
    }
    float newPositionY = enemy->getLastStepY() + additionalHeight;
    enemy->setPosition(Point(enemy->getPosition().x, newPositionY));
    
    // update firing
    if (enemy->fire(dt)) {
        CommonObj* bulletObject = new CommonObj();
        if (enemy->getEnemyName() == EQUIPMENT_WARRIOR) {
            bulletObject->initWithFrame(FRAME_BULLET_4);
        } else {
            bulletObject->initWithFrame(FRAME_BULLET_1);
        }
        bulletObject->getBody()->setVelocity(Point(enemy->getFireX(), enemy->getFireY()));
        Point vectorToTurtle = Point(this->turtle->getPosition() - enemy->getPosition());
        
        // provide shuriken rotation speed
        if (enemy->getEnemyName() == EQUIPMENT_WARRIOR) {
            if (vectorToTurtle.x < 0) {
                bulletObject->getBody()->setRotationVelocity(-CONFIG_ROTATION_SPEED_SHURIKEN);
            } else {
                bulletObject->getBody()->setRotationVelocity(CONFIG_ROTATION_SPEED_SHURIKEN);
            }
        }
        
        if (vectorToTurtle.x < 0) {
            bulletObject->getBody()->setVelocity(Point(-CONFIG_FIRE_SPEED_WARRIOR, 0));
            bulletObject->setPosition(Point(enemy->getPosition().x - enemy->getRectBody()->getWidth() / 2 - bulletObject->getRectBody()->getWidth() / 2, enemy->getPosition().y - CONFIG_MOVE_Y_WARRIOR_BULLET));
            bulletObject->getBody()->setFlipX(true);
        } else {
            bulletObject->getBody()->setVelocity(Point(CONFIG_FIRE_SPEED_WARRIOR, 0));
            bulletObject->setPosition(Point(enemy->getPosition().x + enemy->getRectBody()->getWidth() / 2 + bulletObject->getRectBody()->getWidth() / 2, enemy->getPosition().y - CONFIG_MOVE_Y_WARRIOR_BULLET));
            bulletObject->getBody()->setFlipX(false);
        }
        this->bulletObjects->pushBack(bulletObject);
        enemy->play(SPRITE_TYPE_MAIN, enemy->getStateIdle());
        
        DataEvent* dataEvent = new DataEvent();
        dataEvent->setEventCode(EVT_NEW_BULLET);
        dataEvent->setArgumentReference(bulletObject);
        gameModel->fireEvent(dataEvent);
    }
    
    if (enemy->getFireable()) {
        RectBody* visualRange = enemy->getVisualRange();
        visualRange->setPosition(enemy->getPosition());
        RectBody* turtleBody = turtle->getRectBody();
        if (CPointUtil::b2Interact(visualRange, turtleBody)) {
            if (enemy->getCurrentDelayToFire() == -1) {
                if (turtleBody->getPosition().x > enemy->getPosition().x && !enemy->getBody()->getFlipX()) {
                    enemy->setCurrentDelayToFire(CONFIG_DELAY_PER_FIRE_WARRIOR);
                }
                if (turtleBody->getPosition().x < enemy->getPosition().x && enemy->getBody()->getFlipX()) {
                    enemy->setCurrentDelayToFire(CONFIG_DELAY_PER_FIRE_WARRIOR);
                }
            }
        } else { 
            enemy->setCurrentDelayToFire(-1);
        }
    }
    
    // update body trap with turtle
    if (enemy->getTrappable()) {
        if (CPointUtil::b2Interact(enemy->getRectBody(), this->turtle->getRectBody())) {
            this->turtle->setDead(true);
        }
    }
    
    if (enemy->getCurrentDelayToFire() > 0) {
        enemy->getBody()->setVelocity(Point::ZERO);

        frameStateLoop = false;
        frameState = enemy->getStateThrow();
    }
    
    // update future body
    if (enemy->getMoveState() == MOVE_RIGHT) {
        futureBody->setWidth(10);
        futureBody->setHeight(enemyBody->getHeight());
        futureBody->setPosition(enemy->getBody()->getPosition() + Point(enemyBody->getWidth() / 2 + 5, 0));
    }
    if (enemy->getMoveState() == MOVE_LEFT) {
        futureBody->setWidth(10);
        futureBody->setHeight(enemyBody->getHeight());
        futureBody->setPosition(enemy->getBody()->getPosition() + Point(-enemyBody->getWidth() / 2 - 5, 0));
    }
    
    if (enemy->getMoveState() == MOVE_NONE) {
        futureBody->setWidth(0);
        futureBody->setHeight(0);
        futureBody->setPosition(enemy->getBody()->getPosition());
    }
    
    // update enemy limit
    bool futureBodyInteract = false;
    Point enemyViewPoint = this->getViewPoint(enemy->getPosition());
    for (auto iter = this->lands->begin(); iter != this->lands->end(); iter++) {
        Obj* landObj = *iter;
        if (!this->canSee2(landObj->getRectBody(), enemyViewPoint)) {
            continue;
        }
        RectBody* land = landObj->getRectBody();
        if (land->getPlatform()) {
            continue;
        }
        
        if (!futureBodyInteract) {
            futureBodyInteract = CPointUtil::b2Interact(futureBody, land);
        }
        
        TouchDirection touchDirection;
        float limitValue;
        CPointUtil::checkLimit(enemyBody, land, touchDirection, limitValue);
        if (touchDirection == TOUCH_LEFT) {
            // turn when reach limit
            enemyBody->setPosition(Point(limitValue, enemyBody->getPosition().y));
            enemy->setMoveState(MOVE_LEFT);
        } else if (touchDirection == TOUCH_RIGHT) {
            // turn when reach limit
            enemyBody->setPosition(Point(limitValue, enemyBody->getPosition().y));
            enemy->setMoveState(MOVE_RIGHT);
        } else if (touchDirection == TOUCH_UP) {
            // check falling
            enemyBody->setPosition(Point(enemyBody->getPosition().x, limitValue));
            enemy->setCurrentElapsedJumpTime(0);
            enemy->setLastStepY(limitValue);
        } else if (touchDirection == TOUCH_DOWN) {
            enemyBody->setPosition(Point(enemyBody->getPosition().x, limitValue));
        }
    }
    
    // turn head when future body not interact with land
    if (!futureBodyInteract) {
        if (enemy->getMoveState() == MOVE_LEFT) {
            enemy->setMoveState(MOVE_RIGHT);
        } else if (enemy->getMoveState() == MOVE_RIGHT) {
            enemy->setMoveState(MOVE_LEFT);
        }
    }
    
    // update animation
    if (frameStateLoop) {
        enemy->playLoop(SPRITE_TYPE_MAIN, frameState);
    } else {
        enemy->play(SPRITE_TYPE_MAIN, frameState);
    }
}

void Level::updateViewPoint() {
    Size winSize = Director::getInstance()->getWinSize();
    Rect worldRect = Rect(0, 0, this->mapSize.width, this->mapSize.height);
    
    int x = MAX(this->turtle->getPosition().x, worldRect.origin.x + winSize.width / 2);
    int y = MAX(this->turtle->getPosition().y, worldRect.origin.y + winSize.height / 2);
    x = MIN(x, (worldRect.origin.x + worldRect.size.width) - winSize.width / 2);
    y = MIN(y, (worldRect.origin.y + worldRect.size.height) - winSize.height / 2);
    Point actualPosition = Point(x, y);
    
    Point centerOfView = Point(winSize.width / 2, winSize.height / 2);
    Point viewPoint = centerOfView - actualPosition;
    
    this->viewPoint = viewPoint;
}

Point Level::getViewPoint(const Point& position) {
    Size winSize = Director::getInstance()->getWinSize();
    Rect worldRect = Rect(0, 0, this->mapSize.width, this->mapSize.height);
    
    int x = MAX(position.x, worldRect.origin.x + winSize.width / 2);
    int y = MAX(position.y, worldRect.origin.y + winSize.height / 2);
    x = MIN(x, (worldRect.origin.x + worldRect.size.width) - winSize.width / 2);
    y = MIN(y, (worldRect.origin.y + worldRect.size.height) - winSize.height / 2);
    Point actualPosition = Point(x, y);
    
    Point centerOfView = Point(winSize.width / 2, winSize.height / 2);
    Point viewPoint = centerOfView - actualPosition;
    return viewPoint;
}

bool Level::checkEquip() {
    bool retValue = false;
    GameModel* gameModel = GameModel::getInstance();
    if (!this->turtle->getDead() &&
        !this->turtle->getDisappear()) {
        
        string oldEquiment = this->turtle->getEquipment();
        
        // check if have equippable enemy near turtle
        EnemyObj* equippedEnemy = NULL;
        for (auto iter = this->enemyObjects->begin(); iter != this->enemyObjects->end(); iter++) {
            EnemyObj* enemy = *iter;
            if (CPointUtil::b2Interact(enemy->getRectBody(), this->turtle->getRectBody())) {
                if (!enemy->getTrappable() && enemy->getEquipable()) {
                    enemy->setEquipable(false);
                    this->turtle->equip(enemy->getEnemyName());
                    equippedEnemy = enemy;
                    break;
                }
            }
        }
        
        // if yes, enemy would be destroy, otherwise, turtle equip nothing
        if (equippedEnemy != NULL) {
            DataEvent* dataEvent = new DataEvent();
            dataEvent->setEventCode(EVT_ENEMY_LOST_EQUIPMENT);
            dataEvent->setArgumentReference(equippedEnemy);
            gameModel->fireEvent(dataEvent);
            this->enemyObjects->eraseObject(equippedEnemy);
        } else {
            this->turtle->equip(EQUIPMENT_NONE);
        }
        
        // check if new equipment differ old equipment
        if (oldEquiment != this->turtle->getEquipment()) {
            this->turtle->setMoveSpeed(CONFIG_MOVE_SPEED);
            this->turtle->setJumpHeight(CONFIG_JUMP_HEIGHT);
            retValue = true;
            
            if (this->turtle->getEquipment() == EQUIPMENT_NONE) {
                this->turtle->jump(CONFIG_SUDDEN_JUMP_HEIGHT);
            } else if (this->turtle->getEquipment() == EQUIPMENT_FAST) {
                this->turtle->setJumpHeight(CONFIG_FAST_JUMP_HEIGHT);
                this->turtle->setMoveSpeed(CONFIG_FAST_MOVE_SPEED);
            } else if (this->turtle->getEquipment() == EQUIPMENT_STRENGTH) {
                this->turtle->setMoveSpeed(CONFIG_FAST_MOVE_SPEED);
            } else if (this->turtle->getEquipment() == EQUIPMENT_WARRIOR) {
                this->turtle->setMoveSpeed(CONFIG_FAST_MOVE_SPEED);
            } else if (this->turtle->getEquipment() == EQUIPMENT_ANGEL) {
                this->turtle->setJumpHeight(CONFIG_ANGEL_JUMP_HEIGHT);
                this->turtle->setMoveSpeed(CONFIG_ANGEL_MOVE_SPEED);
            } else if (this->turtle->getEquipment() == EQUIPMENT_NINJA) {
                this->turtle->setMoveSpeed(CONFIG_FAST_MOVE_SPEED);
            }
        }
    }
    return retValue;
}

void Level::turtleFire() {
    if (this->turtle->getDead() ||
        this->turtle->getDisappear() ||
        this->turtle->getEquipment() != EQUIPMENT_WARRIOR) {
        
        return;
    }
 
    GameModel* gameModel = GameModel::getInstance();
    CommonObj* bulletObject = new CommonObj();
    bulletObject->initWithFrame(FRAME_BULLET_4);
    bulletObject->setValueString(BULLET_TYPE_ENEMY);
    
    this->turtle->setThrowing(true);
    this->turtle->setCurrentThrowDelay(this->turtle->getThrowDelay());
    this->turtle->play(SPRITE_TYPE_MAIN, FRAME_TURTLE_IDLE_WARRIOR);
    
    if (this->turtle->getBody()->getFlipX()) {
        bulletObject->getBody()->setRotationVelocity(-CONFIG_ROTATION_SPEED_SHURIKEN);
        bulletObject->getBody()->setVelocity(Point(-CONFIG_FIRE_SPEED_TURTLE, 0));
        bulletObject->getBody()->setFlipX(true);

        bulletObject->setPosition(Point(this->turtle->getPosition().x - this->turtle->getRectBody()->getWidth() / 2 - bulletObject->getRectBody()->getWidth() / 2, this->turtle->getPosition().y));
    } else {
        bulletObject->getBody()->setRotationVelocity(CONFIG_ROTATION_SPEED_SHURIKEN);
        bulletObject->getBody()->setVelocity(Point(CONFIG_FIRE_SPEED_TURTLE, 0));
        
        bulletObject->setPosition(Point(this->turtle->getPosition().x + this->turtle->getRectBody()->getWidth() / 2 + bulletObject->getRectBody()->getWidth() / 2, this->turtle->getPosition().y));
    }
    
    this->bulletObjects->pushBack(bulletObject);
    
    DataEvent* dataEvent = new DataEvent();
    dataEvent->setEventCode(EVT_NEW_BULLET);
    dataEvent->setArgumentReference(bulletObject);
    gameModel->fireEvent(dataEvent);
}

bool Level::canSee(RectBody* body) {
    Size displayResolutionSize = Director::getInstance()->getWinSize();
    
    float maxRight = body->getPosition().x + body->getWidth() / 2;
    float minLeft = body->getPosition().x - body->getWidth() / 2;
    float maxUp = body->getPosition().y + body->getHeight() / 2;
    float minDown = body->getPosition().y - body->getHeight() / 2;
    
    float maxDisplayX = -this->viewPoint.x + displayResolutionSize.width;
    float maxDisplayY = -this->viewPoint.y + displayResolutionSize.height;
    if ((maxDisplayX - minLeft) > 0 &&
        (maxDisplayX - maxRight) < displayResolutionSize.width &&
        (maxDisplayY - minDown) > 0 &&
        (maxDisplayY - maxUp) < displayResolutionSize.height) {
        
        return true;
    }
    
    return false;
}

bool Level::canSee2(RectBody* body, const Point& viewPoint) {
    Size displayResolutionSize = Director::getInstance()->getWinSize();
    
    float maxRight = body->getPosition().x + body->getWidth() / 2;
    float minLeft = body->getPosition().x - body->getWidth() / 2;
    float maxUp = body->getPosition().y + body->getHeight() / 2;
    float minDown = body->getPosition().y - body->getHeight() / 2;
    
    float maxDisplayX = -viewPoint.x + displayResolutionSize.width;
    float maxDisplayY = -viewPoint.y + displayResolutionSize.height;
    if ((maxDisplayX - minLeft) > 0 &&
        (maxDisplayX - maxRight) < displayResolutionSize.width &&
        (maxDisplayY - minDown) > 0 &&
        (maxDisplayY - maxUp) < displayResolutionSize.height) {
        
        return true;
    }
    
    return false;
}